package com.wx.lock.zooKeeper;

import lombok.Data;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.util.Collections;
import java.util.List;

import static com.wx.lock.zooKeeper.ZookeeperClient.GROUP_PATH;


/**
 * @author wx
 * @Description: 分布式锁
 * @date 2018/11/29 002918:45
 */
@Data
public class DistributedLock implements DistributedLockInterface {
    /**
     * zk客户端
     */
    private ZooKeeper zk = new ZookeeperClient().coonect();
    /**
     * 子节点前缀
     */
    public static final String SUB_PATH = GROUP_PATH + "/sub";
    /**
     * 当前节点名称
     */
    private String selfPath;
    /**
     * 监听节点名称
     */
    private String waiPath;
    /**
     * 线程名称
     */
    private String THREAND_NAME = Thread.currentThread().getName();
    /**
     * 分布式锁业务接口
     */
    private DoTemplate doTemplate;


    public DistributedLock(DoTemplate doTemplate) {
        this.doTemplate = doTemplate;
    }


    /**
     * @param
     * @description: 获取锁
     * @author: wx
     * @date: 2018/11/30 0030
     * @return: boolean
     */
    @Override
    public boolean getLock() {
        //创建无权限、临时序号子节点
        try {
            selfPath = zk.create(SUB_PATH, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            //验证当前节点是否是最小节点
            if (checkMinPath()) {
                return true;
            }
            return false;
        } catch (KeeperException e) {
            e.printStackTrace();
            System.out.println(THREAND_NAME + "创建子节点失败");
            return false;
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println(THREAND_NAME + "创建子节点失败");
            return false;
        }
    }

    /**
     * @param
     * @description: 删除节点
     * @author: wx
     * @date: 2018/11/29 0029
     * @return: void
     */
    @Override
    public void unLock() {
        try {
            if (zk.exists(selfPath, false) == null) {
                System.out.println(THREAND_NAME + ":" + selfPath + "节点不存在");
                return;
            }
            zk.delete(selfPath, -1);
            System.out.println(THREAND_NAME + ":" + "删除" + selfPath + "节点");
            zk.close();
        } catch (KeeperException e) {
            e.printStackTrace();
            System.out.println("删除或关闭zk失败");
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("删除或关闭zk失败");
        }
    }

    /**
     * @param
     * @description: 验证当前节点是否是最小节点
     * @author: wx
     * @date: 2018/11/29 0029
     * @return: boolean
     */
    public boolean checkMinPath() {
        try {
            //获取父节点下所有子节点信息
            List<String> subPathList = zk.getChildren(GROUP_PATH, false);
            //集合正序排序
            Collections.sort(subPathList);
            //获取当前节点的下标
            int index = subPathList.indexOf(selfPath.substring(GROUP_PATH.length() + 1));
            switch (index) {
                case -1:
                    System.out.println(THREAND_NAME + ":" + selfPath + "节点不存在");
                    return false;
                case 0:
                    System.out.println(THREAND_NAME + ":" + selfPath + "当前节点是最小节点，获取锁成功");
                    return true;
                default:
                    //获取上一个节点名称
                    waiPath = GROUP_PATH + "/" + subPathList.get(index - 1);
                    try {
                        //监听上一个节点
                        zk.getData(waiPath, new LockWatcher(this), new Stat());
                        System.out.println(THREAND_NAME + "获取锁失败,监听上一个节点" + waiPath);
                        return false;
                    } catch (KeeperException e) {
                        //如果上一个节点不存在则会报异常,递归执行当前方法
                        System.out.println(THREAND_NAME + "监听的上个节点已经消失，重新执行checkMinPath方法");
                        return checkMinPath();
                    }

            }
        } catch (KeeperException e) {
            e.printStackTrace();
            System.out.println(THREAND_NAME + "获取父节点下所有子节点失败");
            return false;
        } catch (InterruptedException e) {
            System.out.println(THREAND_NAME + "获取父节点下所有子节点失败");
            e.printStackTrace();
            return false;
        }
    }

    /**
     * @param
     * @description: 运行具体逻辑方法
     * @author: wx
     * @date: 2018/11/30 0030
     * @return: void
     */
    public void run() {
        doTemplate.dodo();
    }
}
